<?php
// Tim'Roster
// Copyright (c) 2011 Nicolas Nallet <aspgic2@gmail.com>
//
// This file is part of Tim'Roster.
//
// Tim'Roster is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Tim'Roster is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA

$xml_config = simplexml_load_file('config.xml');

include_once($_SERVER["DOCUMENT_ROOT"].$xml_config->roster_root_directory.'/data_access/user_DA.php');
include_once($_SERVER["DOCUMENT_ROOT"].$xml_config->roster_root_directory.'/technical_class/language/language.php');
include_once($_SERVER["DOCUMENT_ROOT"].$xml_config->roster_root_directory.'/technical_class/mail/Mail_Sender.php');


class User {
    const SALT_LENGTH = 40;
    const TEMPORARY_BAN_DURATION = 15; //minutes
    const RESET_FAILED_LOGGING_COUNT_TIME = 15; //minutes
    const MAX_FAILED_LOGGING = 3;

    const CHARACTERS = 'AZERTYUIOPQSDFGHJKLMWXCVBNazertyuiopqsdfghjklmwxcvbn0123456789';

    const GUEST_GROUP_ID = 1;
    const USER_GROUP_ID = 2;
    const ADMIN_GROUP_ID = 3;


    /**
     * (PHP 5)<br/>
     * Get site salt
     * @return string
     */
    private static function get_site_salt(){
        $xml = simplexml_load_file('config.xml');
        return $xml->site_salt;
    }

    /**
     * (PHP 5)<br/>
     * Check if user exists in DB
     * @param string $username
     * @return bool
     */
    public static function user_exists($username){
        $da = new user_DA();
        return $da->user_exists($username);
    }

    /**
     * (PHP 5)<br/>
     * Check if email exists in DB
     * @param string $email
     * @return bool
     */
    public static function email_exists($email){
        $da = new user_DA();
        return $da->email_exists($email);
    }

    /**
     * (PHP 5)<br/>
     * Check if username is well formated
     * @param string $username
     * @return bool
     */
    public static function check_username_format($username){
        return preg_match('/^[0-9a-zA-Z]{4,32}$/',$username);
    }

    /**
     * (PHP 5)<br/>
     * Check if password is well formated
     * @param string $password
     * @return bool
     */
    public static function check_password_format($password){
        return preg_match('/^[0-9a-zA-Z]{6,32}$/',$password);
    }

    /**
     * (PHP 5)<br/>
     * Check if email is well formated
     * @param string $email
     * @return bool
     */
    public static function check_email_format($email){
        return preg_match('/^[a-z0-9A-Z._+-]+@[a-z0-9_-]{2,}\.[a-z]{2,4}$/', $email);
    }

    /**
     * (PHP 5)<br/>
     * Check if session token is well formated
     * @param string $token
     * @return bool
     */
    public static function check_session_token($token){
        return preg_match('/^[0-9a-fA-F]{1,}$/',$token);
    }

    /**
     * (PHP 5)<br/>
     * Check if activation token is well formated
     * @param string $token
     * @return bool
     */
    public static function check_activation_token($token){
        return preg_match('/^[0-9a-fA-F]{1,}$/',$token);
    }

    /**
     * (PHP 5)<br/>
     * Get all user in DB
     * @param $group_id
     * @return array of User
     */
    public static function get_user_list($group_id){
        $da = new user_DA();
        return $da->get_user_list($group_id);
    }

    /**
     * (PHP 5)<br/>
     * Get a user in DB
     * @param int $id
     * @return User
     */
    public static function get_user($id){
        $da = new user_DA();
        return $da->get_user($id);
    }


    /**
     * (PHP 5)<br/>
     * Get a user id with username or email
     * @param string $user_name_or_email
     * @return int
     */
    public static function get_user_id_by_name_or_email($user_name_or_email){
        if (isset($user_name_or_email)){
            $da = new user_DA();
            $id = $da->get_user_id_by_name_or_email($user_name_or_email);
        }

        return (isset($id) ? $id : -1);
    }

    /**
     * (PHP 5)<br/>
     * Get current logged user id
     * @return int
     */
    public static function get_current_user_id(){
        if ((isset($_SESSION['user_id'])) && (isset( $_SESSION['token']))){
            $da = new user_DA();

            //check if the user is logged and if the token contains only hexadecimal number
            if (($da->is_valid_token($_SESSION['user_id'], $_SESSION['token'])) && (User::check_session_token($_SESSION['token']))){
                return $_SESSION['user_id'];
            }
        }

        return -1;
    }

    /**
     * Make secure password with random salt.
     * (same password in database has different hash value)
     *
     * @param $pass
     * @param $salt      public salt value which is random for every password
     *                       to make sure that same password twice in database have different hash
     * @param $site_salt private salt value specific for website
     * @return string length is SALT_LENGTH+40 (sha1 string length)
     */
    private static function make_password($pass, $salt='', $site_salt=''){
        if($salt==''){
            // generate random salt for password
            $salt = substr(sha1(microtime().microtime()), 0, User::SALT_LENGTH);
        }else{
            $salt = substr($salt, 0, User::SALT_LENGTH);
        }
        return $salt.sha1($salt.$pass.$site_salt);
    }

    /**
     * (PHP 5)<br/>
     * Register user in DB
     * @param string $username
     * @param string $password
     * @param string $email
     */
    public static function register_user($username, $password, $email){

        if ((User::check_username_format($username)) && (User::check_password_format($password))
                    && (User::check_email_format($email))){

            if ((!User::user_exists($username)) && (!User::email_exists($email))) {
                $username = strtolower(trim($username));
                $email = strtolower(trim($email));
                $password = User::make_password($password, '', User::get_site_salt());
                $activation_token = sha1(microtime().$user_name.microtime());;

                $da = new user_DA();

                $new_user_id = $da->register_user($username, $password, $email, $activation_token);

                $_SESSION['user_id'] = $new_user_id;
            }
        }

        return (isset($new_user_id) ? $new_user_id : null);
    }

    /**
     * (PHP 5)<br/>
     * Active an account
     * @param string $username
     * @param string $email
     * @param string $activation_token
     * @return bool
     */
    public static function user_activation($username, $email, $activation_token){
        if ((User::check_username_format($username))
            && (User::check_email_format($email))
            && (User::check_activation_token($activation_token))) {

            $user = User::get_user(User::get_user_id_by_name_or_email($username));

            if ((isset($user)) && ($user->get_name() == $username)
                    && ($user->get_email() == $email)
                    && ($user->get_activation_token() == $activation_token)) {

                $da = new user_DA;
                $da->user_activation($user->get_id());
                return true;
            }
        }
        
        return false;
    }

    /**
     * (PHP 5)<br/>
     * Change user password
     * @param int $user_id
     * @param string $new_password
     * @return bool
     */
    public static function change_password($user_id, $old_password, $new_password){
        $u = User::get_user($user_id);

        if ( (isset($u))
                && ((User::verify_pass($u->get_name(), $old_password) != -1) || (User::is_user_authorize(User::ADMIN_GROUP_ID))) ){
            $new_password = User::make_password($new_password, '', User::get_site_salt());

            $da = new user_DA();
            $da->change_password($user_id, $new_password);
            return true;
        }

        return false;
    }

    /**
     * (PHP 5)<br/>
     * Generate a new password for a user
     * @param string $user_name_or_email
     * @return bool
     */
    public static function password_recover($user_name_or_email){
        $u = User::get_user(User::get_user_id_by_name_or_email($user_name_or_email));

        if (isset($u)) {
            $new_password = substr(str_shuffle(str_repeat(User::CHARACTERS,10)),0,10);
            $new_encrypted_password =  User::make_password($new_password, '', User::get_site_salt());

            $da = new user_DA();
            $da->change_password($u->get_id(), $new_encrypted_password);

            Mail_Sender::send_user_password_recovery_mail($u->get_id(), $new_password);

            return true;
        }
        return false;
    }

    /**
     * (PHP 5)<br/>
     * Log off the current user
     */
    public static function logoff(){
        $da = new user_DA();
        $da->suppress_token($_SESSION['user_id'], $_SESSION['token']);
        $_SESSION['user_id'] = null;
        $_SESSION['user_name'] = null;
        $_SESSION['token'] = null;
        $_SESSION['guild_id'] = null;
        $_SESSION['referer'] = null;

        User::suppress_expired_token();
    }

    /**
     * (PHP 5)<br/>
     * Log on the current user
     * @param string $username
     * @param string $password
     * @return bool
     */
    public static function login($username, $password){
        User::suppress_expired_token();
        $username = strtolower(trim($username));
        if (User::check_username_format($username)) {
            $user_id = User::verify_pass($username, $password);

            if ($user_id != -1){
                $da = new user_DA();
                $da->update_user_last_login($username);

                $_SESSION['user_id'] = $user_id;
                $_SESSION['user_name'] = $username;
                //create session token
                $_SESSION['token'] = User::generate_session_token($user_id, $username);
                return true;
            }
        }

        return false;
    }

    /**
     * (PHP 5)<br/>
     * Clean expired session token  in DB
     */
    private static function suppress_expired_token(){
        $da = new user_DA();
        $da->suppress_expired_token();
    }

    /**
     * (PHP 5)<br/>
     * Check if it's the good password
     * @param string $username
     * @param string $pass
     * @return int user_id
     */
    private static function verify_pass($username, $pass){
        $da = new user_DA();
        $result = $da->get_user_password($username);

        $salt = substr($result[0]['password'], 0, User::SALT_LENGTH);

        // workaround to support old passwords without site salt value - can be removed after all users pw_expired are set to '0'
        $pw = $result[0]['password'];
        $p = self::make_password($pass, $salt, User::get_site_salt()); // with site salt - this SHOULD be default

        return ($pw == $p) ? $result[0]['id'] : -1;
    }

    /**
     * (PHP 5)<br/>
     * Create a session token
     * @param int $user_id
     * @param string $user_name
     * @return string
     */
    private static function generate_session_token($user_id, $user_name){
        $token = sha1(microtime().$user_name.microtime());

        $da = new user_DA();
        $da->create_token($user_id, $token);

        return $token;
    }

    /**
     * (PHP 5)<br/>
     * Store referer url
     */
    public static function store_url_referer(){
        $url = parse_url($_SERVER['REQUEST_URI']);
        $_SESSION['referer'] = 'http://'.$_SERVER['HTTP_HOST'].$url['path'];

        if (isset($url['query'])){
            $_SESSION['referer'] .= '?'.$url['query'];
        }
    }

    /**
     * (PHP 5)<br/>
     * Get referer url
     * @return string 
     */
    public static function get_url_referer(){
        return (isset($_SESSION['referer']) ? $_SESSION['referer'] : null);
    }

    /**
     * (PHP 5)<br/>
     * Check if the current user account is active
     * @return bool
     */
    public static function is_user_active(){
        $u = User::get_user(User::get_current_user_id());

        if ((isset($u)) && ($u->get_active())){
            return true;
        }

        return false;
    }

    /**
     * (PHP 5)<br/>
     * Check if the current user is logged
     * @return bool
     */
    public static function is_user_logged(){
        $da = new user_DA();

        if ((User::get_current_user_id() != -1) && (User::is_user_active())){
            //add refresh session token expiration date
            $da->refresh_token_expiration_date($_SESSION['user_id'], $_SESSION['token']);
            return true;
        }

        return false;
    }

    /**
     * (PHP 5)<br/>
     * Check if the current user has the auhorization
     * @param int $authorization_needed (User::GUEST_GROUP_ID, User::USER_GROUP_ID, User::ADMIN_GROUP_ID)
     * @return bool
     */
    public static function is_user_authorize($authorization_needed){
        $da = new user_DA();

        return ((User::is_user_logged()) && ($da->get_user_group($_SESSION['user_id']) >= $authorization_needed));
    }

    /**
     * (PHP 5)<br/>
     * Check if the current user is banned
     * @param string $username
     * @return bool
     */
    public static function is_user_banned($username){
        $da = new user_DA();
        return $da->is_user_banned($username);
    }

    /**
     * (PHP 5)<br/>
     * Increments the failed logging counter
     * @param string $username
     * @return int current attempt count
     */
    public static function add_failed_logging_attempt($username){
        $da = new user_DA();

        $da->add_failed_logging_attempt($username, User::RESET_FAILED_LOGGING_COUNT_TIME);
        $attempt = $da->get_failed_logging_attempt_count($username, User::RESET_FAILED_LOGGING_COUNT_TIME);

        if ($attempt >= User::MAX_FAILED_LOGGING){
             $da->ban_user($username, User::TEMPORARY_BAN_DURATION);
        }

        return $attempt;
    }

    /**
     * (PHP 5)<br/>
     * Get the daban date of a user
     * @param string $username
     * @return datetime
     */
    public static function get_user_deban_date($username){
        $da = new user_DA();
        return $da->get_deban_date($username);
    }

    /**
     * (PHP 5)<br/>
     * Update the group of a user
     * @param int $user_id
     * @param int $group_id
     */
    public static function update_user_group($user_id, $group_id){
        $da = new user_DA();
        $da->update_user_group($user_id, $group_id);
    }

    /**
     * (PHP 5)<br/>
     * Construct a User instance
     * @param int $id
     * @param string $name
     * @param string $email
     * @param datetime $creation_date
     * @param int $login_failed_count
     * @param datetime $last_login_failed_date
     * @param bool $active
     * @param string $activation_token
     * @param datetime $deban_date
     * @param int $group_id
     * @param datetime $last_login
     */
    public function User($id, $name, $email, $creation_date, $login_failed_count, $last_login_failed_date, $active, $activation_token, $deban_date, $group_id, $last_login){
        $this->id = $id;
        $this->name = $name;
        $this->email = $email;
        $this->creation_date = $creation_date;
        $this->login_failed_count = $login_failed_count;
        $this->last_login_failed_date = $last_login_failed_date;
        $this->active = $active;
        $this->deban_date = $deban_date;
        $this->group_id = $group_id;
        $this->last_login = $last_login;
        $this->activation_token = $activation_token;
    }

    private $id;
    /**
     * (PHP 5)<br/>
     * Get the user id
     * @return int
     */
    public function get_id(){
        return $this->id;
    }

    private $name;
    /**
     * (PHP 5)<br/>
     * Get the user name
     * @return string
     */
    public function get_name(){
        return $this->name;
    }

    private $email;
    /**
     * (PHP 5)<br/>
     * Get the user email
     * @return string
     */
    public function get_email(){
        return $this->email;
    }

    private $creation_date;
    /**
     * (PHP 5)<br/>
     * Get the user creation date
     * @return datetime
     */
    public function get_creation_date(){
        return $this->creation_date;
    }

    private $login_failed_count;
    /**
     * (PHP 5)<br/>
     * Get the user login failed count
     * @return int
     */
    public function get_login_failed_count(){
        return $this->login_failed_count;
    }

    private $last_login_failed_date;
    /**
     * (PHP 5)<br/>
     * Get the user last login failed date
     * @return datetime
     */
    public function get_last_login_failed_date(){
        return $this->last_login_failed_date;
    }

    private $active;
    /**
     * (PHP 5)<br/>
     * Get the user active status
     * @return bool
     */
    public function get_active(){
        return $this->active;
    }

    private $activation_token;
    /**
     * (PHP 5)<br/>
     * Get the user activation token
     * @return string
     */
    public function get_activation_token(){
        return $this->activation_token;
    }

    private $deban_date;
    /**
     * (PHP 5)<br/>
     * Get the user deban date
     * @return datetime
     */
    public function get_deban_date(){
        return $this->deban_date;
    }

    private $group_id;
    /**
     * (PHP 5)<br/>
     * Get the user group id
     * @return int
     */
    public function get_group_id(){
        return $this->group_id;
    }

    private $last_login;
    /**
     * (PHP 5)<br/>
     * Get the user last login
     * @return datetime
     */
    public function get_last_login(){
        return $this->last_login;
    }

    /**
     * (PHP 5)<br/>
     * Get if the user is banned
     * @return bool
     */
    public function get_is_banned(){
        if (User::is_user_banned($this->get_name())) {
            return Language::get_instance()->get_label('lb_yes');
        }
        else {
            return Language::get_instance()->get_label('lb_no');
        }
    }

    /**
     * (PHP 5)<br/>
     * Get the user group label
     * @return string
     */
    public function get_group_label(){
        switch ($this->group_id) {
            case User::USER_GROUP_ID : return Language::get_instance()->get_label('lb_base_user_group');
                break;
            case User::ADMIN_GROUP_ID : return Language::get_instance()->get_label('lb_admin_user_group');
                break;
            default: return Language::get_instance()->get_label('lb_no_user_group');
                break;
        }
    }
}


class User_Group {
    /*
     * (PHP 5)<br/>
     * Get all user group
     */
    public static function get_user_group_list(){
        $da = new user_DA();
        return $da->get_user_group_list();
    }

    /**
     * (PHP 5)<br/>
     * Construct a User_Group instance
     * @param int $id
     * @param string $label
     */
    public function User_Group($id, $label){
        $this->id = $id;
        $this->label = $label;
    }

    private $id;
    /**
     * (PHP 5)<br/>
     * Get the user group id
     * @return int
     */
    public function get_id(){
       return $this->id;
    }

    private $label;
    /**
     * (PHP 5)<br/>
     * Get the user group label
     * @return int
     */
    public function get_label(){
        return $this->label;
    }
}
?>
